package ar.edu.itba.pod.Legajo47342.communication;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import ar.edu.itba.pod.Legajo47342.Node;
import ar.edu.itba.pod.Legajo47342.simulation.SimulationManagerImpl;
import ar.edu.itba.pod.simul.communication.AgentDescriptor;
import ar.edu.itba.pod.simul.communication.NodeAgentLoad;
import ar.edu.itba.pod.simul.communication.SimulationCommunication;
import ar.edu.itba.pod.simul.simulation.Agent;

public class SimulationCommunicationImpl implements SimulationCommunication {

	private Node node;
	private List<AgentDescriptor> descriptors;

	public SimulationCommunicationImpl(Node node) throws RemoteException {
		super();
		this.node = node;
		this.descriptors = new ArrayList<AgentDescriptor>();
		
		UnicastRemoteObject.exportObject(this, 0);
	}

	@Override
	public void startAgent(AgentDescriptor descriptor) throws RemoteException {
		NodeAgentLoad load;
		
		synchronized (node.getLoads()) {
			load = node.getLoads().get(node.getNodeId());
		}
		
		int newCant = 1;
		Agent ag = descriptor.build();
		
		ag.onBind(node.getSimulManager().simulation());
		ag.start();
		((SimulationManagerImpl)node.getSimulManager()).storeAgent(ag);
		
		if (load != null) {
			newCant += load.getNumberOfAgents();
			load.setNumberOfAgents(newCant);
			
		} else {
			load = new NodeAgentLoad(node.getNodeId(), newCant);
		}
		
		descriptors.add(descriptor);
		nodeLoadModified(load);
		
		//aca no balance. si se llama este metodo es que alguien 
		//ya dijo que el agente deberia correr aca
		
	}

	@Override
	public NodeAgentLoad getMinimumNodeKnownLoad() throws RemoteException {
		node.updateLoads();
		
		NodeAgentLoad minLoad = node.getLoads().get(node.getNodeId());
		System.out.println("LOCAL LOAD: "+minLoad.getNumberOfAgents());	
		for (NodeAgentLoad load : node.getLoads().values()) {
			if (minLoad.getNumberOfAgents() > load.getNumberOfAgents()) {
				minLoad = load;
			}
		}
		System.out.println("MIN LOAD: "+minLoad.getNumberOfAgents()+" en: "+minLoad.getNodeId());
		return minLoad;
	}

	@Override
	public void nodeLoadModified(NodeAgentLoad newLoad) throws RemoteException {
		synchronized (node.getLoads()) {
			node.getLoads().put(newLoad.getNodeId(), newLoad);
		}

	}

	@Override
	public synchronized Collection<AgentDescriptor> migrateAgents(int numberOfAgents)
			throws RemoteException {
		List<AgentDescriptor> desc = new ArrayList<AgentDescriptor>();
		List<Agent> agents = (List<Agent>) node.getSimulManager().getAgents();
		
		Agent a;
		
		for (int i = 0; i < numberOfAgents; i++) {
			a = agents.get(i);
			
			synchronized (a) {
				a.finish();
				try {
					a.wait();
				} catch (InterruptedException e) {
					System.out.println("Interrupted while finishing an agent");
				}
			}
			
			desc.add(a.getAgentDescriptor());
			node.getSimulManager().removeAgent(a);
		}
		
		descriptors.removeAll(desc);
		
		NodeAgentLoad load = node.getLoads().get(node.getNodeId());
		load.setNumberOfAgents(node.getSimulManager().getAgents().size());
		
		node.getLoads().put(node.getNodeId(), load);
		
		System.out.println("Tamano de desc: "+desc.size());
		return desc;
		
	}

	public Set<AgentDescriptor> getDescriptors() {
		Set<AgentDescriptor> resp = new HashSet<AgentDescriptor>();
		resp.addAll(descriptors);
		
		return resp;
	}
	
}
